if (window.global && global.require) {
	try {
		window.ffi = global.require('ffi-napi')
		window.ref = global.require('ref-napi')
		window.Struct = global.require('ref-struct-napi')
	} catch (error) {
		console.error('load ffi Library error: ', error)
	}

	const {Struct, ref, ffi} = window
	try {
		window.TagEnrollStatus = Struct({
			iErrCode: ref.types.int,
			iCur: ref.types.int,
			iTotal: ref.types.int,
			iScore: ref.types.int,
			iArea: ref.types.int,
			iCurFingerprintID: ref.types.int
		})
		window.cardDll = ffi.Library('dll/HxgcDeviceApi_J15A.dll', {
			CarderOpen: ['int', ['int', 'int']],
			CarderClose: ['int', []],
			GetModuleVer: ['int', ['int', 'string']],
			GetCardStatus: ['int', ['uchar*']],
			CardActive: ['int', ['uchar', 'uchar*', 'int*']],
			CardAPDU: ['int', ['uchar', 'uchar*', 'int', 'uchar*', 'int*', 'double']]
		})
		window.fpDll1 = ffi.Library('dll/M2_YD_ModuleCtrl_R.dll', {
			DllVersion: ['int', ['pointer', 'int']],
			StartMonitor: ['void', []],
			StopMonitor: ['void', []],
			GetFwVersion: ['int', ['pointer']],
			EnterEnroll: ['int', ['int*', 'pointer', 'uchar']],
			EnrollStatus: ['int', ['pointer']],
			CancelOperator: ['int', []],
			DownloadTemplate: ['int', ['int', 'pointer', 'pointer', 'pointer']]
		})
		window.fpDll2 = ffi.Library('dll/M2_YD_ModuleCtrl2_R.dll', {
			DllVersion: ['int', ['pointer', 'int']],
			StartMonitor: ['void', []],
			StopMonitor: ['void', []],
			GetFwVersion: ['int', ['pointer']],
			EnterEnroll: ['int', ['int*', 'pointer', 'uchar']],
			EnrollStatus: ['int', ['pointer']],
			CancelOperator: ['int', []],
			DownloadTemplate: ['int', ['int', 'pointer', 'pointer', 'pointer']]
		})
		window.fpDll = window.fpDll2
	} catch (error) {
		console.error('ffi Library', error)
	}

	const {TagEnrollStatus} = window
	// 指纹录入错误码
	const ERR_CODE = {
		0x00: '操作成功',
		0xa0: '参数错误',
		0xa1: '未检测到有效的指纹录入硬件',
		0xa2: '通讯成功，但数据为空',
		0xa3: '通讯失败',
		0xa4: '通讯超时',

		0x38: '湿手指',
		0x39: '指纹录制有效区域太小',
		0x3a: '指纹录制图像质量太差',
		0x85: '操作被取消'
	}

	window.FP_ERR_CODE = ERR_CODE

	window.DllLibrary = {
		// ----------- 指纹录入器dll方法 --------------
		_iTemplateID: null,
		// 获取dll版本
		DllVersion() {
			const buf = Buffer.alloc(64)
			const errCode = window.fpDll.DllVersion(buf, buf.byteLength)
			return {
				errCode,
				errMsg: ERR_CODE[errCode],
				data: ref.readCString(buf)
			}
		},

		// 监控串口
		StartMonitor(isD5FpLock) {
			// 根据型号加载指纹 dll
			window.fpDll = isD5FpLock ? window.fpDll2 : window.fpDll1
			const errCode = window.fpDll.StartMonitor()
			return {
				errCode,
				errMsg: ERR_CODE[errCode],
				data: null
			}
		},

		// 取消监控串口
		StopMonitor() {
			const errCode = window.fpDll.StopMonitor()
			return {
				errCode,
				errMsg: ERR_CODE[errCode],
				data: null
			}
		},

		// 获取模组版本，确保指纹录入器已连接
		GetFwVersion() {
			const buf = Buffer.alloc(64)
			const errCode = window.fpDll.GetFwVersion(buf)
			return {
				errCode,
				errMsg: ERR_CODE[errCode],
				data: ref.readCString(buf)
			}
		},

		// 开始录入指纹
		EnterEnroll(callback) {
			const tempId = ref.alloc('int')
			window._callback = callback
			window.ffiCallback = ffi.Callback('void', [ref.refType(TagEnrollStatus)], callback)
			const errCode = window.fpDll.EnterEnroll(tempId, window.ffiCallback, 0x01)
			window.DllLibrary._iTemplateID = tempId.deref()
			return {
				errCode,
				errMsg: ERR_CODE[errCode],
				data: window.DllLibrary._iTemplateID
			}
		},

		// 主动查询指纹录入状态，暂无需使用
		EnrollStatus() {
			const tagEnrollStatus = new TagEnrollStatus()
			const errCode = window.fpDll.EnrollStatus(tagEnrollStatus.ref())
			return {
				errCode,
				errMsg: ERR_CODE[errCode],
				data: tagEnrollStatus
			}
		},

		// 取消指纹录入
		CancelOperator() {
			const errCode = window.fpDll.CancelOperator()
			return {
				errCode,
				errMsg: ERR_CODE[errCode],
				data: null
			}
		},

		// 下载指纹模板
		DownloadTemplate(secret) {
			const pAesKey = Buffer.from(secret, 'hex')
			const pTemplateBuffer = Buffer.alloc(1024 * 5)
			const iTemplateSize = ref.alloc('int', 1024 * 5)
			const errCode = window.fpDll.DownloadTemplate(window.DllLibrary._iTemplateID, pAesKey, pTemplateBuffer, iTemplateSize)

			// console.log('pTemplateBuffer', pTemplateBuffer)
			// console.log('iTemplateSize', iTemplateSize)
			// console.log('errCode', errCode)
			const buf = pTemplateBuffer.slice(0, iTemplateSize.deref())
			return {
				errCode,
				errMsg: ERR_CODE[errCode],
				hex: buf.toString('hex'),
				data: buf.toString('base64')
			}
		},

		// ----------- 读卡器dll 方法 --------------
		ReadCard() {
			const pi_bySlot = 0xff
			let port
			for (let i = 1001; i < 1017; i++) {
				const errCode = window.cardDll.CarderOpen(i, 0)
				if (errCode === 0) {
					port = i
					break
				}
			}

			if (!port) {
				return {
					errCode: 1,
					errMsg: '未检测到有效的开卡硬件/驱动'
				}
			}

			const po_pbysATR = Buffer.alloc(512)
			const po_piATRSize = ref.alloc('int')
			const errCode = window.cardDll.CardActive(pi_bySlot, po_pbysATR, po_piATRSize)

			if (errCode !== 0) {
				return {
					errCode,
					errMsg: '读卡失败'
				}
			}
			const cardType = po_pbysATR[0]
			// console.log('po_pbysATR', po_pbysATR)
			if (cardType === 0x0b) {
				// B类卡当做身份证处理
				const pi_pbysSendApdu = Buffer.alloc(16)
				pi_pbysSendApdu[0] = 0x00
				pi_pbysSendApdu[1] = 0x36
				pi_pbysSendApdu[2] = 0x00
				pi_pbysSendApdu[3] = 0x00
				pi_pbysSendApdu[4] = 0x08
				const po_pbysRecvApdu = Buffer.alloc(512)
				const po_piRecvSize = ref.alloc('int')

				const errCode = window.cardDll.CardAPDU(pi_bySlot, pi_pbysSendApdu, 5, po_pbysRecvApdu, po_piRecvSize, 3)
				let result
				if (errCode === 0) {
					// 身份证去掉后面2个字节数据
					const length = po_piRecvSize.deref() - 2
					result = po_pbysRecvApdu.slice(0, length).toString('hex')
				}
				return {
					errCode,
					errMsg: errCode ? '读卡失败' : '',
					data: result.toUpperCase(),
					cardType: 2
				}
			} else {
				//  普通卡or鹿客卡

				// 先尝试读CPU卡，若不成功说明为普通卡
				const pi_pbysSendApdu = Buffer.alloc(16)
				const po_pbysRecvApdu = Buffer.alloc(512)
				const po_piRecvSize = ref.alloc('int')
				pi_pbysSendApdu[0] = 0x00
				pi_pbysSendApdu[1] = 0xa4
				pi_pbysSendApdu[2] = 0x04
				pi_pbysSendApdu[3] = 0x00
				pi_pbysSendApdu[4] = 0x08
				pi_pbysSendApdu[5] = 0x4c
				pi_pbysSendApdu[6] = 0x4f
				pi_pbysSendApdu[7] = 0x4f
				pi_pbysSendApdu[8] = 0x43
				pi_pbysSendApdu[9] = 0x4b
				pi_pbysSendApdu[10] = 0x41
				pi_pbysSendApdu[11] = 0x50
				pi_pbysSendApdu[12] = 0x50
				const errCode2 = window.cardDll.CardAPDU(pi_bySlot, pi_pbysSendApdu, 13, po_pbysRecvApdu, po_piRecvSize, 3)

				let result
				let cardType
				if (errCode2 === 0 && po_pbysRecvApdu[0] < 2) {
					// 解析为鹿客CPU卡, 鹿客卡第1个字节要小于2
					const length = 16
					result = po_pbysRecvApdu.slice(2, 2 + length).toString('hex')
					// 若第1个字节要小于2，但读出来都是0, 则提示不合法
					if (result === '00'.repeat(16)) {
						return {
							errCode: 1,
							errMsg: '读卡失败，卡号不合法',
							cardType: null,
							data: null
						}
					}
					cardType = 1
				} else {
					// 解析为普通卡
					const length = po_pbysATR[1]
					result = po_pbysATR.slice(2, length + 2).toString('hex')
					cardType = 3
				}

				return {
					errCode: 0,
					errMsg: `读卡成功: ${errCode2}`,
					cardType,
					data: result.toUpperCase()
				}
			}
		}
	}

	process.on('exit', () => {
		// eslint-disable-next-line no-unused-expressions
		window.ffiCallback
		// eslint-disable-next-line no-unused-expressions
		window._callback
	})
}
